package org.aepage.logmailer;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.LinkedList;

/**
 * Activity that gathers the current logcat and mails it to the specified
 * address.
 *
 * Intent i = new Intent(this, LogMailer.class) ;
 *
 * Log.i("LOGMAILER", "Launching LogMailer activity") ;
 *
 * i.putExtra(LogMailer.EMAIL_ADDRESS_EXTRA, emailAddressText.getText().toString()) ;
 *
 * startActivityForResult(i, LOGMAILER_REQUEST_CODE);
 *
 *
 * Created with IntelliJ IDEA.
 * User: andrew
 * Date: 2/23/14
 * Time: 5:18 PM
 *
 */
public class LogMailer extends Activity {

  /**
   * Extra field to add to launching intent that contains the email
   * to send the log data too
   */
  public static final String EMAIL_ADDRESS_EXTRA = "EMAIL_ADDRESS_EXTRA";


  /**
   * Extra field to add to intent to specify a message to send in the body
   * of the email
   */
  public static final String MESSAGE_TEXT_EXTRA = "MESSAGE_TEXT_EXTRA";

  /**
   * result from activity indicating the log was gathered and sent correctly
   */
  public static final int RESULT_OK = 0;

  /**
   * Error result from the activity indicating that no email was included
   */
  public static final int ERROR_NO_EMAIL_ADDRESS = -1;

  /**
   * an IO error occurred in the gathering or sending of the log
   */
  public static final int IO_ERROR_RESULT = -2;

  /**
   * error occurred in configuring the xml transformer
   */
  public static final int XML_TRANSFORMCONFIG_ERROR_RESULT = -3;

  /**
   * error occurred transforming the xml report
   */
  public static final int XML_TRANSFORM_ERROR_RESULT = -4;

  /**
   * Add to intent to specify the subject of the sent email
   */
  public static final String SUBJECT_EXTRA = "SUBJECT_EXTRA";

  /**
   * Logcat format(-v option) to use
   */
  public static final String LOGCAT_FORMAT_EXTRA = "LOGCAT_FORMAT_EXTRA";

  /**
   * Logcat filter spec
   */
  public static final String LOGCAT_FILTERSPEC_EXTRA = "LOGCAT_FILTERSPEC_EXTRA";

  /**
   * List of filter specs (plural)
   */
  public static final String LOGCAT_FILTERSPECS_EXTRA = "LOGCAT_FILTERSPECS_EXTRA";

  /**
   * Don't actually send the message, just return the result through
   * the result intent.  Largely for testing but may find other uses.
   */
  public static final String NO_SEND_EXTRA = "NO_SEND_EXTRA";


  private String emailAddress ;
  private String subjectText ;
  private String messageText ;

  /**
   * log data passed back to the launching activity through the intent,
   * received with onActivityResult(int requestCode, int resultCode, Intent data)
   */
  public static final String LOG_DATA_EXTRA = "LOG_DATA_EXTRA";
  private int logSizeLimit = 1024*1024 ;
  private boolean noSend;


  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent i = getIntent() ;

    emailAddress = i.getStringExtra(EMAIL_ADDRESS_EXTRA) ;
    if( emailAddress == null ) {
      setResult(ERROR_NO_EMAIL_ADDRESS) ;
      finish();
      return ;
    }

    emailAddress = emailAddress.toString() ;


    subjectText = i.getStringExtra(SUBJECT_EXTRA) ;
    if( subjectText == null )
      subjectText = "Android Log" ;
    subjectText = subjectText.toString() ;

    messageText = i.getStringExtra(MESSAGE_TEXT_EXTRA)  ;
    if( messageText == null )
      messageText = "log data" ;

    noSend = i.getBooleanExtra(NO_SEND_EXTRA, false) ;
  }

  @Override
  protected void onStart() {
    super.onStart();

    try {
      String log = gatherLog() ;

      if( ! noSend )
        sendLog(log) ;

      Intent i = new Intent() ;
      i.putExtra(LOG_DATA_EXTRA, log) ;

      setResult(RESULT_OK, i);
    } catch (IOException e) {
      setResult(IO_ERROR_RESULT);

    }
    finish();

  }

  /**
   * Invoke logcat and gather up our log
   * @return
   * @throws IOException
   */
  private String gatherLog() throws IOException {
    Intent i = getIntent() ;
    LinkedList<String> args = new LinkedList<String>();

    args.add("logcat");
    args.add("-d") ;

    String format = i.getStringExtra(LOGCAT_FORMAT_EXTRA) ;
    if( format != null ) {
      args.add("-v") ;
      args.add(format) ;
    }

    String filterSpec = i.getStringExtra(LOGCAT_FILTERSPEC_EXTRA) ;
    if( filterSpec != null ) {
      args.add(filterSpec) ;
    }

    String filterSpecs[] = i.getStringArrayExtra(LOGCAT_FILTERSPECS_EXTRA) ;
    if( filterSpecs != null ) {
      for( String s: filterSpecs )
        args.add(s);
    }

    Process proc = Runtime.getRuntime().exec(args.toArray(new String[args.size()])) ;
    File f = File.createTempFile("logcatdata", ".txt") ;

    BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())) ;
    String line ;
    StringBuilder b = new StringBuilder() ;

    int sz = 0 ;
    while( sz < logSizeLimit && (line = in.readLine()) != null ) {
      b.append(line) ;
      b.append("\n") ;
      sz += line.length() + 1 ;
    }

    return b.toString() ;

  }

  /**
   * Compose our log into an XML attachment and then send it to the
   * requested email addresses
   * @param log
   */
  private void sendLog(String log) throws FileNotFoundException {

    Intent i = new Intent(Intent.ACTION_SEND) ;

    String version = Build.VERSION.RELEASE ;
    Document doc ;
    DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance() ;
    try {
      doc = bf.newDocumentBuilder().newDocument() ;
    } catch (ParserConfigurationException e) {
      throw new RuntimeException(e) ;
    }

    String appVersion = "unknown" ;
    try {
      PackageInfo info = getPackageManager().getPackageInfo(getPackageName(),0) ;
      appVersion = info.versionName ;

      if( appVersion == null )
        appVersion = "unknown" ;
    } catch (PackageManager.NameNotFoundException e) {
      appVersion = "app version failed" ;
    }

    Element root = doc.createElement("logmail") ;

    root.setAttribute("manufacturer", Build.MANUFACTURER);
    root.setAttribute("device", Build.DEVICE);
    root.setAttribute("package", getPackageName());
    root.setAttribute("versionName", appVersion);

    doc.appendChild(root) ;

    root.appendChild(doc.createCDATASection(log)) ;

    try {
      File path = Environment.getExternalStorageDirectory() ;
      path = new File(path.getAbsolutePath() + "/LogMailer") ;
      boolean result = path.mkdir() ;
      path.setReadable(true, false) ;

      File f = new File(path.getAbsolutePath() + "/data.xml" ) ;
      FileOutputStream os = new FileOutputStream(f) ;
      TransformerFactory tFactory = TransformerFactory.newInstance() ;

      Transformer t = tFactory.newTransformer()  ;
      t.setOutputProperty(OutputKeys.INDENT, "yes");
      DOMSource domSrc = new DOMSource(doc) ;
      StreamResult sresult = new StreamResult(f) ;
      t.transform(domSrc, sresult);

      i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f)) ;
      i.putExtra(Intent.EXTRA_SUBJECT, subjectText) ;
      i.putExtra(Intent.EXTRA_EMAIL, new String[] {emailAddress}) ;
      i.putExtra(Intent.EXTRA_TEXT, messageText) ;
      i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;
      i.setType("text/xml") ;
      startActivity(Intent.createChooser(i, "Export Data"));

    } catch (TransformerConfigurationException e) {
      setResult(XML_TRANSFORMCONFIG_ERROR_RESULT) ;
    } catch (TransformerException e) {
      setResult(XML_TRANSFORM_ERROR_RESULT) ;
    }




  }
}
